﻿namespace MicroCloud.AspNetCore.SignalR.Internal
{
    /// <summary>
    /// 类型化客户端构建器
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal static class TypedClientBuilder<T>
    {
        //字段常量
        private static readonly Lazy<Func<IClientProxy, T>> _builder = new(() => GenerateClientBuilder());
        private static readonly PropertyInfo CancellationTokenNoneProperty = typeof(CancellationToken).GetProperty("None", BindingFlags.Static | BindingFlags.Public);
        //private const string ClientModuleName = "Microsoft.AspNetCore.SignalR.TypedClientBuilder";

        #region "静态方法"
        #region "构建"
        /// <summary>
        /// 构建
        /// </summary>
        /// <param name="proxy">用于调用hub方法的代理抽象</param>
        /// <returns></returns>
        public static T Build(IClientProxy proxy)
        {
            return _builder.Value(proxy);
        }
        #endregion
        #region "验证"
        /// <summary>
        /// 验证
        /// </summary>
        public static void Validate()
        {
            Func<IClientProxy, T> func = _builder.Value;
        }
        #endregion

        #endregion

        #region "私有静态方法"
        #region "生成客户端构建器"
        /// <summary>
        /// 生成客户端构建器
        /// </summary>
        /// <returns></returns>
        private static Func<IClientProxy, T> GenerateClientBuilder()
        {
            VerifyInterface(typeof(T));
            Type clientType = GenerateInterfaceImplementation(AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Microsoft.AspNetCore.SignalR.TypedClientBuilder"), AssemblyBuilderAccess.Run).DefineDynamicModule("Microsoft.AspNetCore.SignalR.TypedClientBuilder"));
            return proxy => (T)Activator.CreateInstance(clientType, proxy);
        }
        #endregion
        #region "生成接口实现"
        /// <summary>
        /// 生成接口实现
        /// </summary>
        /// <param name="moduleBuilder">定义并表示动态组件中的模块</param>
        /// <returns></returns>
        private static Type GenerateInterfaceImplementation(ModuleBuilder moduleBuilder)
        {
            TypeBuilder type = moduleBuilder.DefineType("Microsoft.AspNetCore.SignalR.TypedClientBuilder." + typeof(T).Name + "Impl", TypeAttributes.Public, typeof(object), new Type[1]
            {
                typeof (T)
            });
            FieldBuilder fieldBuilder = type.DefineField("_proxy", typeof(IClientProxy), FieldAttributes.Private);
            BuildConstructor(type, fieldBuilder);
            foreach (MethodInfo allInterfaceMethod in GetAllInterfaceMethods(typeof(T)))
            {
                BuildMethod(type, allInterfaceMethod, fieldBuilder);
            }
            return type.CreateTypeInfo();
        }
        #endregion
        #region "获取所有接口方法"
        /// <summary>
        /// 获取所有接口方法
        /// </summary>
        /// <param name="interfaceType">接口类型</param>
        /// <returns></returns>
        private static IEnumerable<MethodInfo> GetAllInterfaceMethods(Type interfaceType)
        {
            Type[] typeArray = interfaceType.GetInterfaces();
            int index;
            for (index = 0; index < typeArray.Length; ++index)
            {
                foreach (MethodInfo allInterfaceMethod in GetAllInterfaceMethods(typeArray[index]))
                {
                    yield return allInterfaceMethod;
                }
            }
            typeArray = null;
            MethodInfo[] methodInfoArray = interfaceType.GetMethods();
            for (index = 0; index < methodInfoArray.Length; ++index)
            {
                yield return methodInfoArray[index];
            }
            methodInfoArray = null;
        }
        #endregion
        #region "生成构造函数"
        /// <summary>
        /// 生成构造函数
        /// </summary>
        /// <param name="type">在运行时期间定义和创建类的新实例</param>
        /// <param name="proxyField">代理字段</param>
        private static void BuildConstructor(TypeBuilder type, FieldInfo proxyField)
        {
            MethodBuilder methodBuilder = type.DefineMethod(".ctor", MethodAttributes.Public | MethodAttributes.HideBySig);
            ConstructorInfo constructor = typeof(object).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, (Binder)null, Array.Empty<Type>(), null);
            methodBuilder.SetReturnType(typeof(void));
            methodBuilder.SetParameters(typeof(IClientProxy));
            ILGenerator ilGenerator = methodBuilder.GetILGenerator();
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Call, constructor);
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldarg_1);
            ilGenerator.Emit(OpCodes.Stfld, proxyField);
            ilGenerator.Emit(OpCodes.Ret);
        }
        #endregion
        #region "生成方法"
        /// <summary>
        /// 生成方法
        /// </summary>
        /// <param name="type">在运行时期间定义和创建类的新实例</param>
        /// <param name="interfaceMethodInfo">接口方法信息</param>
        /// <param name="proxyField">代理字段</param>
        private static void BuildMethod(TypeBuilder type, MethodInfo interfaceMethodInfo, FieldInfo proxyField)
        {
            MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask;
            ParameterInfo[] parameters = interfaceMethodInfo.GetParameters();
            Type[] array1 = parameters.Select(param => param.ParameterType).ToArray<Type>();
            MethodBuilder methodBuilder = type.DefineMethod(interfaceMethodInfo.Name, attributes);
            MethodInfo method = typeof(IClientProxy).GetMethod("SendCoreAsync", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, (Binder)null, new Type[3]
            {
                typeof (string),
                typeof (object[]),
                typeof (CancellationToken)
            }, null);
            methodBuilder.SetReturnType(interfaceMethodInfo.ReturnType);
            methodBuilder.SetParameters(array1);
            string[] array2 = array1.Where(p => p.IsGenericParameter).Select(p => p.Name).Distinct().ToArray();
            if (array2.Any())
            {
                methodBuilder.DefineGenericParameters(array2);
            }
            ILGenerator ilGenerator = methodBuilder.GetILGenerator();
            ilGenerator.DeclareLocal(typeof(object[]));
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldfld, proxyField);
            ilGenerator.Emit(OpCodes.Ldstr, interfaceMethodInfo.Name);
            ilGenerator.Emit(OpCodes.Ldc_I4, parameters.Length);
            ilGenerator.Emit(OpCodes.Newarr, typeof(object));
            ilGenerator.Emit(OpCodes.Stloc_0);
            for (int index = 0; index < array1.Length; ++index)
            {
                ilGenerator.Emit(OpCodes.Ldloc_0);
                ilGenerator.Emit(OpCodes.Ldc_I4, index);
                ilGenerator.Emit(OpCodes.Ldarg, index + 1);
                ilGenerator.Emit(OpCodes.Box, array1[index]);
                ilGenerator.Emit(OpCodes.Stelem_Ref);
            }
            ilGenerator.Emit(OpCodes.Ldloc_0);
            ilGenerator.Emit(OpCodes.Call, CancellationTokenNoneProperty.GetMethod);
            ilGenerator.Emit(OpCodes.Callvirt, method);
            ilGenerator.Emit(OpCodes.Ret);
        }
        #endregion
        #region "验证接口"
        /// <summary>
        /// 验证接口
        /// </summary>
        /// <param name="interfaceType">接口类型</param>
        private static void VerifyInterface(Type interfaceType)
        {
            if (!interfaceType.IsInterface)
            {
                //throw new InvalidOperationException("Type must be an interface.");
                //throw new InvalidOperationException("类型必须是接口");
                throw new InvalidOperationException(I18N.T("类型 {0} 必须是接口", interfaceType.FullName));
            }
            if (interfaceType.GetProperties().Length != 0)
            {
                //throw new InvalidOperationException("Type must not contain properties.");
                //throw new InvalidOperationException("类型不能包含属性");
                throw new InvalidOperationException(I18N.T("类型 {0} 不能包含属性", interfaceType.FullName));
            }
            if (interfaceType.GetEvents().Length != 0)
            {
                //throw new InvalidOperationException("Type must not contain events.");
                //throw new InvalidOperationException("类型不能包含事件");
                throw new InvalidOperationException(I18N.T("类型 {0} 不能包含事件", interfaceType.FullName));
            }
            foreach (MethodInfo method in interfaceType.GetMethods())
            {
                VerifyMethod(interfaceType, method);
            }
            foreach (Type interfaceType1 in interfaceType.GetInterfaces())
            {
                VerifyInterface(interfaceType1);
            }
        }
        #endregion
        #region "验证方法"
        /// <summary>
        /// 验证方法
        /// </summary>
        /// <param name="interfaceType">接口类型</param>
        /// <param name="interfaceMethod">接口方法</param>
        private static void VerifyMethod(Type interfaceType, MethodInfo interfaceMethod)
        {
            if (interfaceMethod.ReturnType != typeof(Task))
            {
                //throw new InvalidOperationException("Cannot generate proxy implementation for '" + typeof(T).FullName + "." + interfaceMethod.Name + "'. All client proxy methods must return '" + typeof(Task).FullName + "'.");
                //throw new InvalidOperationException($"无法为 {typeof(T).FullName}.{interfaceMethod.Name} 生成代理实现，所有客户端代理方法都必须返回 {typeof(Task).FullName} 。");
                throw new InvalidOperationException(I18N.T("无法为 {0}.{1} 生成代理实现，所有客户端代理方法都必须返回 {2}。", typeof(T).FullName, interfaceMethod.Name, typeof(Task).FullName));
            }
            foreach (ParameterInfo parameter in interfaceMethod.GetParameters())
            {
                if (parameter.IsOut)
                {
                    //throw new InvalidOperationException("Cannot generate proxy implementation for '" + typeof(T).FullName + "." + interfaceMethod.Name + "'. Client proxy methods must not have 'out' parameters.");
                    //throw new InvalidOperationException($"无法为 {typeof(T).FullName}.{interfaceMethod.Name} 生成代理实现，客户端代理方法不能有“out”参数。");
                    throw new InvalidOperationException(I18N.T("无法为 {0}.{1} 生成代理实现，客户端代理方法不能有“out”参数。", typeof(T).FullName, interfaceMethod.Name));
                }
                if (parameter.ParameterType.IsByRef)
                {
                    //throw new InvalidOperationException("Cannot generate proxy implementation for '" + typeof(T).FullName + "." + interfaceMethod.Name + "'. Client proxy methods must not have 'ref' parameters.");
                    //throw new InvalidOperationException($"无法为 {typeof(T).FullName}.{interfaceMethod.Name} 生成代理实现，客户端代理方法不能有“ref”参数。");
                    throw new InvalidOperationException(I18N.T("无法为 {0}.{1} 生成代理实现，客户端代理方法不能有“ref”参数。", typeof(T).FullName, interfaceMethod.Name));

                }
            }
        }
        #endregion

        #endregion

    }

}
